/* libwatopt++ - Optimization Toolkit
 *
 * Copyright (c) 2007 Christopher Alexander Watford
 * <christopher.watford@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * $Id: sa_schedules.cpp 2 2007-05-06 18:01:15Z christopher.watford $
 */

#include <cstdlib>
#include <cmath>

#include <sa.hpp>
#include <sa_schedules.hpp>

#include "utils.hpp"

namespace watopt
{

   ///
   /// ExponentialSteppedCooling
   ///

   ExponentialSteppedCooling::ExponentialSteppedCooling(double alpha, size_t interval)
   : _m_Alpha(alpha), _m_Interval(interval)
   {
   }

   ExponentialSteppedCooling::ExponentialSteppedCooling(const ExponentialSteppedCooling& sched)
   : _m_Alpha(sched._m_Alpha), _m_Interval(sched._m_Interval)
   {
   }

   ExponentialSteppedCooling&
   ExponentialSteppedCooling::operator=(const ExponentialSteppedCooling& sched)
   {
	   _m_Alpha = sched._m_Alpha;
	   _m_Interval = sched._m_Interval;
	   return *this;
   }

   double
   ExponentialSteppedCooling::Step(size_t step, double temp, size_t maxStep, double maxTemp) const
   {
	   if(temp < 2.22044604925031e-016)
		   return 0.0;

	   if(step % _m_Interval)
		   return temp;
   	
	   return (temp * std::pow(_m_Alpha,(double)_m_Interval));
   }

   ///
   /// LinearSteppedCooling
   ///

   LinearSteppedCooling::LinearSteppedCooling(double alpha, size_t interval)
   : _m_Alpha(alpha), _m_Interval(interval)
   {

   }

   LinearSteppedCooling::LinearSteppedCooling(const LinearSteppedCooling& sched)
   : _m_Alpha(sched._m_Alpha), _m_Interval(sched._m_Interval)
   {

   }

   LinearSteppedCooling&
   LinearSteppedCooling::operator=(const LinearSteppedCooling& sched)
   {
	   _m_Alpha = sched._m_Alpha;
      _m_Interval = sched._m_Interval;
	   return *this;
   }

   double
   LinearSteppedCooling::Step(size_t step, double temp, size_t maxStep, double maxTemp) const
   {
	   if(temp < 2.22044604925031e-016)
		   return 0.0;

      if(step % _m_Interval)
         return temp;

	   return (temp - _m_Alpha);
   }

   ///
   /// LinearCooling
   ///

   LinearCooling::LinearCooling(double alpha)
   : _m_Alpha(alpha)
   {
   }

   LinearCooling::LinearCooling(const LinearCooling& sched)
   : _m_Alpha(sched._m_Alpha)
   {
   }

   LinearCooling&
   LinearCooling::operator=(const LinearCooling& sched)
   {
	   _m_Alpha = sched._m_Alpha;
	   return *this;
   }

   double
   LinearCooling::Step(size_t step, double temp, size_t maxStep, double maxTemp) const
   {
      if(temp < 2.22044604925031e-016)
         return 0.0;

	   return (temp - _m_Alpha);
   }

   ///
   /// ExponentialCooling
   ///

   ExponentialCooling::ExponentialCooling(double alpha)
   : _m_Alpha(alpha)
   {
   }

   ExponentialCooling::ExponentialCooling(const ExponentialCooling& sched)
   : _m_Alpha(sched._m_Alpha)
   {
   }

   ExponentialCooling&
   ExponentialCooling::operator=(const ExponentialCooling& sched)
   {
	   _m_Alpha = sched._m_Alpha;
	   return *this;
   }

   double
   ExponentialCooling::Step(size_t step, double temp, size_t maxStep, double maxTemp) const
   {
	   if(temp < 2.22044604925031e-016)
		   return 0.0;

	   return (temp * _m_Alpha);
   }

   ///
   /// DampedOscillatorCooling
   ///

   DampedOscillatorCooling::DampedOscillatorCooling(double oscillations, double stretch)
   : _m_Osc(oscillations), _m_Stretch(stretch)
   {
   }

   DampedOscillatorCooling::DampedOscillatorCooling(const DampedOscillatorCooling& sched)
   : _m_Osc(sched._m_Osc), _m_Stretch(sched._m_Stretch)
   {
   }

   DampedOscillatorCooling&
   DampedOscillatorCooling::operator=(const DampedOscillatorCooling& sched)
   {
	   _m_Osc = sched._m_Osc;
	   _m_Stretch = sched._m_Stretch;
	   return *this;
   }

   double
   DampedOscillatorCooling::Step(size_t step, double temp, size_t maxStep, double maxTemp) const
   {
	   // (10000sin(x/2)/(exp(x/6000)))^2
	   return std::pow((maxTemp*std::sin(_m_Osc*step)/(std::exp(step/_m_Stretch))),2);
   }

}